package com.duxd.springsecurityfinal.springsecurityfinal.security.config;

import com.duxd.springsecurityfinal.springsecurityfinal.pojo.User;
import com.duxd.springsecurityfinal.springsecurityfinal.response.Response;
import com.duxd.springsecurityfinal.springsecurityfinal.response.ResponseBuild;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author dong
 * @date 2022/1/9 0009
 */
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {

    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("duxd").password("123456").roles("admin")
                .and()
                .withUser("hu").password("123456").roles("");
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("admin")
                .anyRequest().authenticated().and()

                .formLogin()
                .and().logout().logoutUrl("/logout")
                .addLogoutHandler(new SuccessLogoutHandler())

                .and()
                .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .and().exceptionHandling().authenticationEntryPoint(new MyAuthenticationEntryPoint())
                .accessDeniedHandler(new MyAccessDeniedHandler())

                .and().rememberMe().rememberMeCookieName("rrrrrrr")
                .tokenRepository(new InMemoryTokenRepositoryImpl())
                .rememberMeParameter("rm")
                .and()
                .addFilterAt(create(), UsernamePasswordAuthenticationFilter.class);

    }

    @Override
    public void configure(org.springframework.security.config.annotation.web.builders.WebSecurity web) throws Exception {
        web.ignoring()
                .antMatchers("/*.html", "/*.js", "favicon.ico","/error")
                .requestMatchers(getApplicationContext().getBean(HandlerMethodAnnotationRequestMatcher.class));
                //.and().debug(true); // 测试开启debug
    }

    private RequestBodyUserNamePasswordAuthenticationFilter create() throws Exception {
        RequestBodyUserNamePasswordAuthenticationFilter filter = new RequestBodyUserNamePasswordAuthenticationFilter();
        filter.setAuthenticationManager(authenticationManager());
        filter.setFilterProcessesUrl("/doLogin");
        filter.setPostOnly(true);
        filter.setAuthenticationSuccessHandler(new SuccessHandler());
        filter.setAuthenticationFailureHandler(new FailedHandler());

        return filter;
    }

    static class RequestBodyUserNamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter{

        @Override
        protected String obtainUsername(HttpServletRequest request)  {
            User body = null;
            try {
                body = new ObjectMapper().readValue(request.getReader(), User.class);
                request.setAttribute("user", body);
            } catch (IOException exception) {
                exception.printStackTrace();
            }

            return body.getName();
        }

        @Override
        protected String obtainPassword(HttpServletRequest request) {
             User user = (User) request.getAttribute("user");
             return user.getPassword();
        }
    }

    static class SuccessLogoutHandler implements LogoutHandler{

        @Override
        public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
            Response response1 = ResponseBuild.logout_success.withMessage("logout success！");
            try {
                response.getWriter().write(new ObjectMapper().writeValueAsString(response1));
                response.getWriter().flush();
            } catch (IOException exception) {
                exception.printStackTrace();
            }

        }
    }
    static class SuccessHandler implements AuthenticationSuccessHandler{
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
            Response response1 = ResponseBuild.login_success.withMessage("login success!");
            response.getWriter().write(new ObjectMapper().writeValueAsString(response1));
            response.getWriter().flush();
        }
    }

    // 登录失败处理器
    static class FailedHandler implements AuthenticationFailureHandler {
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
            Response response1 = ResponseBuild.login_failed.withMessage("name or password error, please re-enter!");
            response.getWriter().write(new ObjectMapper().writeValueAsString(response1));
            response.getWriter().flush();
        }
    }

    // 未认证处理器
    static class MyAuthenticationEntryPoint implements AuthenticationEntryPoint{

        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
            Response response1 = ResponseBuild.auth_failed.withMessage("Please login！");
            response.getWriter().write(new ObjectMapper().writer().writeValueAsString(response1));
            response.getWriter().flush();
        }
    }

    // 未有权限时处理器
    static class MyAccessDeniedHandler implements AccessDeniedHandler {

        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
            Response response1 = ResponseBuild.no_authority.withMessage("you has no authority！");
            response.getWriter().write(new ObjectMapper().writer().writeValueAsString(response1));
            response.getWriter().flush();
        }
    }
}
